//brpc的二次封装
//brpc本质上来说--jinxingrpc调用的,但是向谁调用用什么服务管理起来--搭配etcd实现的注册中心管理
//原因:通过注册中心,能够获知谁额能提供什么服务,进而能够连接它发起的这个服务调用

//封装思想:
//主要是管理起来网络通信的信道---将不同的服务节点主机的通信信道连接管理起来
//封装的是服务节点的信道管理,而不是rpc调用服务

//封装:
#pragma once
#include<brpc/channel.h>
#include<string>
#include<vector>
#include<unordered_map>
#include<mutex>
#include"logger.hpp"


//1.指定服务的信道管理类:
//一个服务可能会有多个节点提供服务,每个节点有自己的channel
//建立服务与信道的映射关系,关系是一对多,采用RP轮转的策略进行获取
class ServiceChannel
{
public:
    using ptr = std::shared_ptr<ServiceChannel>;
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    ServiceChannel(const std::string& service_name)
        :_service_name(service_name)
        ,_index(0)
    {}
    //服务上线了一个节点,则调用append新增信道
    void append(const std::string& host)
    {
        //1.创建网络通信信道
        auto channel = std::make_shared<brpc::Channel>();
        brpc::ChannelOptions options;
        options.connect_timeout_ms = -1;  //等待超时时间,-1表示一直等待
        options.timeout_ms = -1; //rpc请求等待超时时间,-1表示一直等待
        options.max_retry = 3;   //请求重试次数
        options.protocol = "baidu_std";   //序列化协议,默认使用baidu_std
        int ret = channel->Init(host.c_str(),&options);
        if(ret == -1)
        {
            LOG_ERROR("初始化{}-{}",_service_name,host);
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        _hosts.insert({host,channel});     //
        _channels.push_back(channel);
    }
    //服务下线了一个节点,则调用removes删除信道
    void remove(const std::string& host)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _hosts.find(host);
        if(it == _hosts.end())
        {
            LOG_WARN("{}-{}节点删除信道时,没有找到信息",_service_name,host);
            return;
        }
        for(auto vit = _channels.begin();vit != _channels.end();vit++)
        {
            if(*vit == it->second)
            {
                _channels.erase(vit);
                break;
            }
        }
        _hosts.erase(it);
    }
    //通过RP轮转策略,获取一个Channel用于发起对应服务的rpc调用
    ChannelPtr choose()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_channels.size() == 0)
        {
            LOG_ERROR("当前没有能够提供{}服务的节点",_service_name);
            return ChannelPtr();
        }
        int32_t idx = _index++ % _channels.size();
        return _channels[idx];
    }
private:
    std::mutex _mutex;
    int32_t _index;  //当前轮转下标计数器
    std::string _service_name; //服务名称
    std::vector<ChannelPtr> _channels;  //当前服务对应的信道集合
    std::unordered_map<std::string,ChannelPtr> _hosts;   //主机地址与信道地址的映射关系

};


//2.总体的服务信道管理类:
//将多个服务的信道管理对象管理起来
class ServiceManager
{
public:
    ServiceManager()
    {}
    using ptr = std::shared_ptr<ServiceManager>;
    //获取能够提供服务的节点信道
    ServiceChannel::ChannelPtr choose(const std::string& service_name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto sit = _services.find(service_name);
        if(sit == _services.end())
        {
            LOG_ERROR("当前没有能够提供{}服务的节点",service_name);
            return ServiceChannel::ChannelPtr();
        }
        return sit->second->choose();
    }
    //声明关注哪些服务的上线和下线
    void declared(const std::string& service_name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _follow_services.insert(service_name);
    }
    //服务上线时调用的回调函数接口,将服务节点管理器来
    void onServiceOnline(const std::string& service_instance,const std::string& host)
    {
        std::string service_name = getServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto fit = _follow_services.find(service_name);
            if(fit == _follow_services.end())
            {
                LOG_DEBUG("{}-{} 服务上线了,当时当前处于未关心状态",service_name,host);
                return;
            }
            //先获取管理对象,没有则创建,有则添加
            auto sit = _services.find(service_name);
            if(sit == _services.end())
            {
                service = std::make_shared<ServiceChannel> (service_name);
                _services.insert({service_name,service});
            }
            else
            {
                service = sit->second;
            }
        }
        if(!service)
        {
            LOG_ERROR("新增{}服务的管理节点失败",service_name);
            return ;
        }
        service->append(host);
        LOG_DEBUG("{}-{}服务上线新节点,进行添加管理",service_name,host);

    }
    //服务下线时调用的回调函数接口,从服务信道管理中,删除指定的节点信道
    void onServiceOffline(const std::string& service_instance,const std::string& host)
    {
        std::string service_name = getServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            //先获取管理对象,没有则创建,有则添加
            auto fit = _follow_services.find(service_name);
            if(fit == _follow_services.end())
            {
                LOG_DEBUG("{}-{} 服务下线了,当时当前处于未关心状态");
                return;
            }
            auto sit = _services.find(service_name);
            if(sit == _services.end())
            {
                LOG_ERROR("删除{}服务节点时,没有找到管理对象",service_name);
                return ;
            }
            service = sit->second;
        }
        service->remove(host);
        LOG_DEBUG("{}-{}服务下线新节点,进行删除管理",service_name,host);
    }
private:
    std::string getServiceName(const std::string& service_instance)
    {
        auto pos = service_instance.find_last_of('/');
        if(pos == std::string::npos)
        {
            return service_instance;
        }
        return service_instance.substr(0,pos);
    }
private:
    std::mutex _mutex;
    std::unordered_set<std::string> _follow_services;
    std::unordered_map<std::string,ServiceChannel::ptr> _services;
};



